home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The CICA Windows Explosion!
/
The CICA Windows Explosion! - Disc 2.iso
/
programr
/
genmix1b.zip
/
GENMIX
/
SRC
/
GENMIX.C
next >
Wrap
C/C++ Source or Header
|
1995-04-15
|
35KB
|
1,465 lines
/* GENMIX.C -- Generic mixer source code
(C) Copyright 1995 Ian A. McLean
See the file README.TXT for license and other information
version 1B -- first beta release
*/
#include "port.h"
// maximum number of mixer channels
#define maxchan 8
static short outputnumchan; // number of mixer channels
static int outputblocksize; // block size in samples
static waveRate outputrate; // output sample rate
static waveBits outputbits; // output bits per sample
static waveSpeak outputspeak; // output number of speakers
static short outputsampsize; // output bytes per sample
static short outputsampshift; // lg(outputsampsize)
static short outputpaused; // output paused flag
static int outputplayahead; // number of blocks to keep in buf
static int outputremix; // immediate remix flag
static mixCodec mixcodecs[mixNumCodec];
static int numcodecs = 0;
/* -------- WAVEFORM RENDERER --------
Copies samples, converting wave format (mono<->stereo, 8<->16),
temporal scaling (11kHz<->22kHz<->44kHz), and applying a mono or
stereo volume scale. Code is described as macros to allow an
80-way unrolling of the main rendering loop. This redundancy allows
the compiler to produce optimized decisionless code specific to each
possible rendering task.
*/
// read source sample
#define readsamp_s8_d8(i) \
i = *(unsigned char*)psrc; \
psrc += sizeof(unsigned char); \
i -= 0x80;
#define readsamp_s8_d16(i) \
i = *(unsigned char*)psrc; \
psrc += sizeof(unsigned char); \
i -= 0x80; \
i <<= 8;
#define readsamp_s16_d8(i) \
i = *(signed short*)psrc; \
psrc += sizeof(signed short);
#define readsamp_s16_d16(i) \
i = *(signed short*)psrc; \
psrc += sizeof(signed short);
#define readsamp(i,sb,db) \
readsamp_s ## sb ## _d ## db ## (i)
// apply volume scale
#define scalesamp(i,vol) \
i *= vol.mul; \
i >>= vol.shift;
// normalize sample
#define normsamp_s8_d8(i) \
i += 0x80;
#define normsamp_s8_d16(i)
#define normsamp_s16_d8(i) \
i >>= 8; \
i += 0x80;
#define normsamp_s16_d16(i)
#define normsamp(i,sb,db) \
normsamp_s ## sb ## _d ## db ## (i)
// write dest sample
#define writesamp_d8(i) \
*(unsigned char*)pdst = (unsigned char) i; \
pdst += sizeof(unsigned char);
#define writesamp_d16(i) \
*(signed short*)pdst = (signed short) i; \
pdst += sizeof(signed short);
#define writesamp(i,db) \
writesamp_d ## db ## (i)
// mono output, stretching
#define writedupmono_dup1(db) \
writesamp(i,db);
#define writedupmono_dup2(db) \
writesamp(i,db); \
writesamp(i,db);
#define writedupmono_dup4(db) \
writesamp(i,db); \
writesamp(i,db); \
writesamp(i,db); \
writesamp(i,db);
#define writedupmono(numdup,db) \
writedupmono_dup ## numdup ## (db)
// stereo output, stretching
#define writedupstereo_dup1(db) \
writesamp(i,db); writesamp(j,db);
#define writedupstereo_dup2(db) \
writesamp(i,db); writesamp(j,db); \
writesamp(i,db); writesamp(j,db);
#define writedupstereo_dup4(db) \
writesamp(i,db); writesamp(j,db); \
writesamp(i,db); writesamp(j,db); \
writesamp(i,db); writesamp(j,db); \
writesamp(i,db); writesamp(j,db);
#define writedupstereo(numdup,db) \
writedupstereo_dup ## numdup ## (db)
// mono input, shrinking
#define readavgmono_avg2(sb,db) \
readsamp(i,sb,db); \
readsamp(j,sb,db); i += j; i >>= 1;
#define readavgmono_avg4(sb,db) \
readsamp(i,sb,db); \
readsamp(j,sb,db); i += j; \
readsamp(j,sb,db); i += j; \
readsamp(j,sb,db); i += j; i >>= 2;
#define readavgmono(numavg,sb,db) \
readavgmono_avg ## numavg ## (sb,db)
// stereo input, shrinking
#define readavgstereo_avg2(sb,db) \
readsamp(i,sb,db); \
readsamp(j,sb,db); \
readsamp(i1,sb,db); i += i1; i >>= 1; \
readsamp(j1,sb,db); j += j1; j >>= 1;
#define readavgstereo_avg4(sb,db) \
readsamp(i,sb,db); \
readsamp(j,sb,db); \
readsamp(i1,sb,db); i += i1; \
readsamp(j1,sb,db); j += j1; \
readsamp(i1,sb,db); i += i1; \
readsamp(j1,sb,db); j += j1; \
readsamp(i1,sb,db); i += i1; i >>= 2; \
readsamp(j1,sb,db); j += j1; j >>= 2;
#define readavgstereo(numavg,sb,db) \
readavgstereo_avg ## numavg ## (sb,db)
// mono to mono copy
#define monotomonodup(numdup,sb,db) \
{ \
register int i; \
readsamp(i,sb,db) \
scalesamp(i,vol.both) \
normsamp(i,sb,db) \
writedupmono(numdup,db) \
}
#define monotomonoavg(numavg,sb,db) \
{ \
register int i,j; \
readavgmono(numavg,sb,db); \
scalesamp(i,vol.both); \
normsamp(i,sb,db); \
writesamp(i,db); \
}
// mono to stereo copy
#define monotostereodup(numdup,sb,db) \
{ \
register int i,j; \
readsamp(i,sb,db) \
j = i; \
scalesamp(i,vol.left); \
scalesamp(j,vol.right); \
normsamp(i,sb,db) \
normsamp(j,sb,db) \
writedupstereo(numdup,db) \
}
#define monotostereoavg(numavg,sb,db) \
{ \
register int i,j; \
readavgmono(numavg,sb,db); \
j = i; \
scalesamp(i,vol.left); \
scalesamp(j,vol.right); \
normsamp(i,sb,db); \
normsamp(j,sb,db); \
writesamp(i,db); \
writesamp(j,db); \
}
// stereo to mono copy
#define stereotomonodup(numdup,sb,db) \
{ \
register int i,j; \
readsamp(i,sb,db) \
readsamp(j,sb,db) \
scalesamp(i,vol.left) \
scalesamp(j,vol.right) \
i += j; \
i >>= 1; \
normsamp(i,sb,db) \
writedupmono(numdup,db); \
}
#define stereotomonoavg(numavg,sb,db) \
{ \
register int i,j,i1,j1; \
readavgstereo(numavg,sb,db); \
scalesamp(i,vol.left); \
scalesamp(j,vol.right); \
i += j; \
i >>= 1; \
normsamp(i,sb,db); \
writesamp(i,db); \
}
// stereo to stereo copy
#define stereotostereodup(numdup,sb,db) \
{ \
register int i,j; \
readsamp(i,sb,db) \
readsamp(j,sb,db) \
scalesamp(i,vol.left) \
scalesamp(j,vol.right) \
normsamp(i,sb,db) \
normsamp(j,sb,db) \
writedupstereo(numdup,db); \
}
#define stereotostereoavg(numavg,sb,db) \
{ \
register int i,j,i1,j1; \
readavgstereo(numavg,sb,db); \
scalesamp(i,vol.left); \
scalesamp(j,vol.right); \
normsamp(i,sb,db); \
normsamp(j,sb,db); \
writesamp(i,db); \
writesamp(j,db); \
}
#define rendsamp(srcspeak,dstspeak,srcbits,dstbits,scaleop,scalefact) \
srcspeak ## to ## dstspeak ## scaleop ## (scalefact,srcbits,dstbits)
// rendering loop
#define rloop \
{ \
while (numsamp) \
{
#define rloopend \
numsamp--; \
} \
break; \
}
#define scaleswitch(sspeak,dspeak,sbits,dbits) \
switch (scale) { \
case -4: rloop rendsamp(sspeak,dspeak,sbits,dbits,avg,4); rloopend; \
case -2: rloop rendsamp(sspeak,dspeak,sbits,dbits,avg,2); rloopend; \
case 1: rloop rendsamp(sspeak,dspeak,sbits,dbits,dup,1); rloopend; \
case 2: rloop rendsamp(sspeak,dspeak,sbits,dbits,dup,2); rloopend; \
case 4: rloop rendsamp(sspeak,dspeak,sbits,dbits,dup,4); rloopend; \
}
#define ifbits(srcspeak,dstspeak) \
if (bitsrc == waveBits8) \
if (bitdst == waveBits8) scaleswitch(srcspeak,dstspeak,8,8) \
else scaleswitch(srcspeak,dstspeak,8,16) \
else \
if (bitdst == waveBits8) scaleswitch(srcspeak,dstspeak,16,8) \
else scaleswitch(srcspeak,dstspeak,16,16)
static void lowRender(pmem psrc, waveRate ratesrc, waveBits bitsrc, waveSpeak speaksrc,
pmem pdst, waveRate ratedst, waveBits bitdst, waveSpeak speakdst,
long numsamp, pmixVol pmv, int scale)
/*
this unrolls into:
4 number of speakers: [mono|stereo] -> [mono|stereo]
* 4 number of sample bits: [8|16] -> [8|16]
* 5 temporal scaling [-4,-2,1,2,4]
= 80 separate rendering loops
in a hierarchy of five decisions (speaksrc,speakdst,bitsrc,bitdst,scale)
*/
{
mixVol vol;
vol = *pmv;
if (speaksrc == waveSpeakMono)
if (speakdst == waveSpeakMono) ifbits(mono,mono)
else ifbits(mono,stereo)
else
if (speakdst == waveSpeakMono) ifbits(stereo,mono)
else ifbits(stereo,stereo)
return;
}
/* -------- DATA SOURCE --------
A stream of samples
*/
typedef enum
{
dsrcTypeSilence,
dsrcTypeSample,
dsrcTypeCodec
} dsrcType;
typedef struct
{
short sampscale;
short sampshift;
pmem pdata;
waveRate rate;
waveBits bits;
waveSpeak speak;
mixVol vol;
} dsrcSample, *pdsrcSample;
typedef struct
{
short sampscale;
pmem pdata;
waveRate rate;
waveBits bits;
waveSpeak speak;
mixVol vol;
/***/
} dsrcCodec, *pdsrcCodec;
typedef union
{
dsrcSample sample;
dsrcCodec codec;
} dsrcUnion, *pdsrcUnion;
typedef struct
{
dsrcType type;
long curpos;
long remain;
dsrcUnion u;
} dsrc, *pdsrc;
static mixVol mixVolNorm = { {1,0},{1,0},{1,0} };
static void silenceRender(pmem pdest, long numsamp)
{
long num4, num1;
unsigned long *plong;
unsigned char *pchar;
num1 = (numsamp << outputsampshift);
num4 = num1 >> 2;
num1 &= 3;
if (outputbits == waveBits8)
{
plong = (unsigned long*) pdest;
while (num4--) *plong++ = 0x80808080;
pchar = (unsigned char*) plong;
while (num1--) *pchar++ = 0x80;
}
else {
plong = (unsigned long*) pdest;
while (num4--) *plong++ = 0x00000000;
pchar = (unsigned char*) plong;
while (num1--) *pchar++ = 0x00;
}
return;
}
static void sampleRender(pdsrcSample pds, long pos, long num, pmem pdest, pmixVol pmv)
{
pmem psrc;
long numrendsrc;
if (pds->sampscale > 0)
{
psrc = pds->pdata + ((pos/pds->sampscale) << pds->sampshift);
numrendsrc = num / pds->sampscale;
if (num % pds->sampscale) numrendsrc++;
lowRender( psrc, pds->rate, pds->bits, pds->speak,
pdest, outputrate, outputbits, outputspeak,
numrendsrc, pmv, (int)pds->sampscale);
}
else {
psrc = pds->pdata + ((-pos*pds->sampscale) << pds->sampshift);
numrendsrc = num * -pds->sampscale;
lowRender( psrc, pds->rate, pds->bits, pds->speak,
pdest, outputrate, outputbits, outputspeak,
num, pmv, (int)pds->sampscale);
}
return;
}
static void codecRender(pdsrcCodec pdc, long pos, long num, pmem pdest, pmixVol pmv)
{
/***/
}
static long dsrcRender(pdsrc pds, pmem pdest, long numsamples, int cangrow, pmixVol pmv)
{
long numrend;
if (cangrow && pds->type==dsrcTypeSilence && pds->remain < numsamples)
pds->remain = numsamples;
numrend = pds->remain;
if (numrend > numsamples) numrend = numsamples;
if (numrend)
{
switch (pds->type)
{
case dsrcTypeSilence:
silenceRender(pdest, numrend);
break;
case dsrcTypeSample:
sampleRender(&pds->u.sample, pds->curpos, numrend, pdest, pmv);
break;
case dsrcTypeCodec:
codecRender(&pds->u.codec, pds->curpos, numrend, pdest, pmv);
break;
}
pds->curpos += numrend;
pds->remain -= numrend;
}
return numrend;
}
static dsrcType dsrcCreate(pdsrc pds, pmixSound pms)
{
if (!pms) {
pds->type = dsrcTypeSilence;
pds->remain = 0;
pds->curpos = 0;
return dsrcTypeSilence;
}
else if (pms->type == mixCodecPCM)
{
pdsrcSample psamp = &pds->u.sample;
long numsamp;
psamp->pdata = pms->pdata;
psamp->rate = pms->rate;
psamp->speak = pms->speak;
psamp->bits = pms->bits;
psamp->vol = pms->vol;
switch (psamp->speak * psamp->bits)
{
case 1: psamp->sampshift = 0; break;
case 2: psamp->sampshift = 1; break;
case 4: psamp->sampshift = 2; break;
}
if (psamp->rate <= outputrate) {
psamp->sampscale = (short)outputrate / (short)psamp->rate;
numsamp = pms->samples * psamp->sampscale;
}
else {
psamp->sampscale = -(short)psamp->rate / (short)outputrate;
numsamp = pms->samples / -psamp->sampscale;
}
pds->remain = numsamp;
pds->curpos = 0;
pds->type = dsrcTypeSample;
return dsrcTypeSample;
}
else {
/***/
pds->type = dsrcTypeCodec;
return dsrcTypeCodec;
}
}
static long dsrcRewind(pdsrc pds, long numsamples)
{
if (pds->curpos >= numsamples)
{
pds->remain += numsamples;
pds->curpos -= numsamples;
}
else {
numsamples = pds->curpos;
pds->remain += numsamples;
pds->curpos = 0;
}
return numsamples;
}
static void dsrcDestroy(pdsrc pds)
{
if (pds->type == dsrcTypeCodec)
{
/***/
}
return;
}
static long dsrcGetCurPos(pdsrc pds)
{
return pds->curpos;
}
static long dsrcGetNumSamples(pdsrc pds)
{
return pds->curpos + pds->remain;
}
static long dsrcGetCurRemain(pdsrc pds)
{
return pds->remain;
}
static void dsrcTrim(pdsrc pds, long maxrewind)
{
if (pds->type == dsrcTypeCodec)
{
/***/
}
return;
}
static void dsrcTruncate(pdsrc pds)
{
pds->remain = 0;
if (pds->type == dsrcTypeCodec)
{
/***/
}
return;
}
static int dsrcContainsPtr(pdsrc pds, pmem p)
{
switch (pds->type)
{
case dsrcTypeSilence:
return 0;
case dsrcTypeSample:
if (p == pds->u.sample.pdata) return 1;
return 0;
case dsrcTypeCodec:
if (p == pds->u.codec.pdata) return 1;
return 0;
}
return 0;
}
static void dsrcGetVol(pdsrc pds, pmixVol pv)
{
switch (pds->type)
{
case dsrcTypeSilence:
*pv = mixVolNorm;
break;
case dsrcTypeSample:
*pv = pds->u.sample.vol;
break;
case dsrcTypeCodec:
*pv = pds->u.codec.vol;
break;
}
return;
}
/* -------- CHANNEL LIST --------
An array of doublely-linked lists.
*/
typedef struct _tagcnode *pcnode;
typedef struct _tagcnode
{
dsrcType type;
dsrc ds;
pcnode pnext;
pcnode pprev;
} cnode;
typedef struct
{
pcnode pfirst;
pcnode plast;
pcnode pcur;
mixVol curvol;
mixVol vol;
} channel, *pchannel;
static channel chan[maxchan];
static void chanInit()
{
pcnode pcn;
pchannel pchan;
short i;
pchan = chan;
for (i=0; i<outputnumchan; i++)
{
pcn = (pcnode) memNew(sizeof(cnode));
pcn->type = dsrcTypeSilence;
dsrcCreate(&pcn->ds, 0);
pcn->pnext = pcn->pprev = 0;
pchan->pfirst = pchan->plast = pchan->pcur = pcn;
pchan->vol = pchan->curvol = mixVolNorm;
pchan++;
}
return;
}
static void chanFree()
{
short i;
pcnode pcn, pcnnext;
for (i=0; i<outputnumchan; i++)
{
pcn = chan[i].pfirst;
while (pcn)
{
pcnnext = pcn->pnext;
dsrcDestroy(&pcn->ds);
memDispose((pmem)pcn);
pcn = pcnnext;
}
}
return;
}
static void chanVerify(short channum, short arerewound)
{
pchannel pchan;
pcnode pcur, pprev;
int foundcur, lastsil;
long totsiz;
long cursize;
pchan = chan + channum;
pcur = pchan->pfirst;
pprev = 0;
foundcur = 0;
totsiz = 0;
lastsil = 0;
while (pcur) {
if (pcur->type == dsrcTypeSilence)
{
if (lastsil) portFatalError(badlog, 0);
lastsil = 1;
} else lastsil = 0;
cursize = dsrcGetNumSamples(&pcur->ds);
if (!arerewound)
if (cursize != dsrcGetCurPos(&pcur->ds) && pcur->pnext)
portFatalError(badlog, 0);
totsiz += cursize;
if (pcur == pchan->pcur) {
if (foundcur) portFatalError(badlog, 0);
foundcur = 1;
}
if (pcur->pprev != pprev) portFatalError(badlog, 0);
// if (!cursize && pcur->pnext) portFatalError(badlog, 0);
if (cursize < 0) portFatalError(badlog, 0);
pprev = pcur;
pcur = pcur->pnext;
}
if (pprev != pchan->plast) portFatalError(badlog, 0);
if (!foundcur) portFatalError(badlog, 0);
if (!arerewound && pchan->pcur != pchan->plast) portFatalError(badlog, 0);
return;
}
static void chanVerifyAll(short arerewound)
{
short c;
for (c=0; c<outputnumchan; c++)
chanVerify(c, arerewound);
return;
}
static void chanTrim(short channum)
{
pcnode pcur, pnext;
long maxsamples, siz;
pchannel pchan;
chanVerify(channum, 0);
maxsamples = (long)outputblocksize * (long)outputplayahead;
pchan = chan + channum;
pcur = pchan->pcur;
siz = dsrcGetCurPos(&pchan->pcur->ds);
siz -= dsrcGetNumSamples(&pchan->pcur->ds);
while (1) {
siz += dsrcGetNumSamples(&pchan->pcur->ds);
if (siz >= maxsamples) break;
pcur = pcur->pprev;
if (!pcur) break;
}
if (pcur) {
pchan->pfirst = pcur;
pnext = pcur->pprev;
pcur->pprev = 0;
pcur = pnext;
}
while (pcur) {
pnext = pcur->pprev;
dsrcDestroy(&pcur->ds);
memDispose((pmem)pcur);
pcur = pnext;
}
dsrcTrim(&pchan->pcur->ds, maxsamples);
chanVerify(channum, 0);
return;
}
static void chanSetVol(short channum, pmixVol pmv)
{
pchannel pchan;
mixVol v;
chanVerify(channum, 1);
pchan = chan + channum;
if (pmv)
pchan->vol = *pmv;
dsrcGetVol(&pchan->pcur->ds, &v);
mixVolMult(&pchan->curvol, &pchan->vol, &v);
chanVerify(channum, 1);
return;
}
static void chanRender(pmem pdest, short channum, long numsamp)
{
pchannel pchan;
pcnode pcur;
long numrend;
int cangrow;
chanVerify(channum, 1);
pchan = chan + channum;
pcur = pchan->pcur;
if (numsamp >0)
while (1)
{
cangrow = (pcur->pnext == 0);
numrend = dsrcRender(&pcur->ds, pdest, numsamp, cangrow, &pchan->curvol);
if (numrend != numsamp)
if (!pcur->pnext)
{
pcur->pnext = (pcnode) memNew(sizeof(cnode));
pcur->pnext->pprev = pcur;
pcur = pcur->pnext;
pchan->plast = pchan->pcur = pcur;
pcur->pnext = 0;
pcur->type = dsrcCreate(&pcur->ds, 0);
}
else pcur = pcur->pnext;
numsamp -= numrend;
if (numsamp <= 0) break;
pdest += (numrend << outputsampshift);
}
chanVerify(channum, 0);
return;
}
static void chanRewind(short channum, long numsamples)
{
pchannel pchan;
pcnode pcur;
long rewind, rewound;
int innew;
pchan = chan + channum;
rewind = numsamples;
pcur = pchan->pcur;
innew = 0;
while (rewind > 0)
{
rewound = dsrcRewind(&pcur->ds, rewind);
if (rewound != rewind)
{
pcur = pcur->pprev;
pchan->pcur = pcur;
if (!pcur) portFatalError(badlog, 0);
innew = 1;
}
rewind -= rewound;
}
chanVerify(channum, 1);
if (innew) chanSetVol(channum, 0);
return;
}
static void chanTruncate(short channum)
{
pcnode plast, pcur;
pchannel pchan;
pchan = chan + channum;
plast = pchan->plast;
pcur = pchan->pcur;
while (plast != pcur)
{
pchan->plast = plast->pprev;
dsrcDestroy(&plast->ds);
memDispose((pmem)plast);
plast = pchan->plast;
if (!plast) portFatalError(badlog, 0);
}
plast->pnext = 0;
dsrcTruncate(&plast->ds);
chanVerify(channum, 0);
return;
}
static void chanAddSound(short channum, pmixSound pms)
{
pchannel pchan;
pcnode psamp, plast;
pchan = chan + channum;
plast = pchan->plast;
psamp = (pcnode) memNew(sizeof(cnode));
pchan->plast = psamp;
psamp->pprev = plast;
plast->pnext = psamp;
psamp->type = dsrcCreate(&psamp->ds, pms);
psamp->pnext = 0;
if (!dsrcGetCurRemain(&pchan->pcur->ds))
pchan->pcur = psamp;
chanVerify(channum, 0);
return;
}
static short chanFindData(pmem pdata)
{
short i;
pchannel pchan;
pcnode pcur;
for (i=0; i<outputnumchan; i++)
{
pchan = chan + i;
pcur = pchan->pfirst;
while (pcur)
{
if (dsrcContainsPtr(&pcur->ds, pdata)) return i;
pcur = pcur->pnext;
}
}
return -1;
}
static short chanIsSilent(short channum)
{
pchannel pchan;
pchan = chan + channum;
if (pchan->pfirst != pchan->plast) return 0;
if (pchan->pcur != pchan->pfirst) portFatalError(badlog, 0);
if (pchan->pcur->type == dsrcTypeSilence) return 1;
return 0;
}
/* -------- 8 BIT MIXER FUNCTION ARRAY --------
An array of functions that mix samples
*/
#define mixer8func(name) \
static void name ( unsigned char *psrc, unsigned char *pdest, \
long numsamples) \
{ \
int i; \
while (numsamples) \
{ \
i = *psrc;
#define mixer8add(num) \
i += *(psrc + mixRingSize*(long)num);
#define mixer8store(numchan) \
i -= (numchan-1) << 7; \
*pdest++ = (unsigned char) i; \
psrc++; numsamples--; \
} \
return; \
}
mixer8func(mix8_1) mixer8store(1)
mixer8func(mix8_2) mixer8add(1) mixer8store(2)
mixer8func(mix8_3) mixer8add(1) mixer8add(2) mixer8store(3)
mixer8func(mix8_4) mixer8add(1) mixer8add(2) mixer8add(3) mixer8store(4)
mixer8func(mix8_5) mixer8add(1) mixer8add(2) mixer8add(3) mixer8add(4)
mixer8store(5)
mixer8func(mix8_6) mixer8add(1) mixer8add(2) mixer8add(3) mixer8add(4)
mixer8add(5) mixer8store(6)
mixer8func(mix8_7) mixer8add(1) mixer8add(2) mixer8add(3) mixer8add(4)
mixer8add(5) mixer8add(6) mixer8store(7)
mixer8func(mix8_8) mixer8add(1) mixer8add(2) mixer8add(3) mixer8add(4)
mixer8add(5) mixer8add(6) mixer8add(7) mixer8store(8)
typedef void (*mixer8fn)(unsigned char *, unsigned char *, long);
mixer8fn mix8[maxchan]= {
mix8_1, mix8_2, mix8_3, mix8_4,
mix8_5, mix8_6, mix8_7, mix8_8
};
/* -------- 16 BIT MIXER FUNCTION ARRAY --------
An array of functions that mix samples
*/
#define mixer16func(name) \
static void name ( signed short *psrc, signed short *pdest, \
long numsamples) \
{ \
int i; \
while (numsamples) \
{ \
i = *psrc;
#define mixer16add(num) \
i += *(psrc + mixRingSize*(long)num/2L);
#define mixer16store \
*pdest++ = (signed short) i; \
psrc++; numsamples--; \
} \
return; \
}
mixer16func(mix16_1) mixer16store
mixer16func(mix16_2) mixer16add(1) mixer16store
mixer16func(mix16_3) mixer16add(1) mixer16add(2) mixer16store
mixer16func(mix16_4) mixer16add(1) mixer16add(2) mixer16add(3) mixer16store
mixer16func(mix16_5) mixer16add(1) mixer16add(2) mixer16add(3) mixer16add(4)
mixer16store
mixer16func(mix16_6) mixer16add(1) mixer16add(2) mixer16add(3) mixer16add(4)
mixer16add(5) mixer16store
mixer16func(mix16_7) mixer16add(1) mixer16add(2) mixer16add(3) mixer16add(4)
mixer16add(5) mixer16add(6) mixer16store
mixer16func(mix16_8) mixer16add(1) mixer16add(2) mixer16add(3) mixer16add(4)
mixer16add(5) mixer16add(6) mixer16add(7) mixer16store
typedef void (*mixer16fn)(signed short *, signed short *, long);
mixer16fn mix16[maxchan]= {
mix16_1, mix16_2, mix16_3, mix16_4,
mix16_5, mix16_6, mix16_7, mix16_8
};
/* -------- MIX RING ARRAY --------
This data structure is an array of ring buffers. There is one ring
buffer per mixer channel. The ring buffer holds samples that have
been scaled to the destination wave format and channel volume. A
current position pointer points to the next sample to be mixed. Start
and end pointers point to the first and last valid samples in the buffer.
One size counter indicates the total number of samples in the buffer
(end minus start), the other indicates the total number of unmixed
samples in the buffer (end minus current).
+-----------------------------------------------------+
|xxxxxxxxxxxxDATADATADATADATADATADATADATADATAxxxxxxxxx|
+-----------------------------------------------------+
^ ^ ^ ^ ^
| | | | |
(begin) (first) (cur) (last) (end)
*/
static pmem pringbegin; // beginning of ring buffer (all)
static pmem pringend; // end of ring buffer (all)
static pmem pringcur; // current position in ring buffer
static pmem pringfirst; // beginning of ring buffer data
static pmem pringlast; // end of ring buffer data
static long ringremain; // number of samples remaining
static long ringtotal; // total samples in buffer
static mixSound ringnewsound[maxchan]; // sound to mix
static short ringisnewsound[maxchan];// sound to mix flag
static short ringnumnewsound; // number of sounds to mix
static long remixtimesound; // total ms rerendering sounds
static long remixtimemix; // total ms remixing ring buffer
static short remixtimenumsound; // number of sounds rerendered
static short remixtimenummix; // number of ring buffer remixes
static void mrInit()
{
pmem pmem;
short i;
// allocate ring buffers
pmem = memNew(mixRingSize * (long)outputnumchan);
// initialize ring buffer, set to empty
ringtotal = ringremain = 0;
pringbegin = pringfirst = pringlast = pringcur = pmem;
pringend = pmem + mixRingSize;
// clear new sound array
for (i=0; i<outputnumchan; i++) ringisnewsound[i] = 0;
ringnumnewsound = 0;
remixtimesound = 0;
remixtimenumsound = 0;
remixtimemix = 0;
remixtimenummix = 0;
return;
}
static void mrFree()
{
// dispose of ring buffers
memDispose(pringbegin);
return;
}
static void mrRenderAll(long numsamples)
{
long del;
long siz;
short i;
del = numsamples - ringremain;
if (del > 0)
{
ringtotal += del;
ringremain += del;
siz = (pringend - pringlast) >> outputsampshift;
if (siz > del) siz = del;
for (i=0; i<outputnumchan; i++)
chanRender(pringlast + mixRingSize*i, i, siz);
pringlast += (siz << outputsampshift);
if (pringlast >= pringend) pringlast = pringbegin;
del -= siz;
if (del > 0)
{
for (i=0; i<outputnumchan; i++)
chanRender(pringlast + mixRingSize*i, i, del);
pringlast += (del << outputsampshift);
}
del = ringtotal - (mixRingSize >> outputsampshift);
if (del > 0)
{
ringtotal -= del;
pringfirst += (del << outputsampshift);
if (pringfirst >= pringend) pringfirst -= mixRingSize;
}
for (i=0; i<outputnumchan; i++)
chanTrim(i);
}
return;
}
static void mrReRender(short channum, long numsamp)
{
long size1;
size1 = (pringend - pringcur) >> outputsampshift;
if (size1 > numsamp) size1 = numsamp;
chanRender(pringcur + mixRingSize * channum, channum, size1);
numsamp -= size1;
if (numsamp > 0)
chanRender(pringbegin + mixRingSize * channum, channum, numsamp);
return;
}
static void mrLowMix(pmem pdest, long numsamp)
{
switch (outputbits)
{
case waveBits8:
mix8[outputnumchan-1]((unsigned char*)pringcur, (unsigned char*)pdest,
numsamp * outputspeak);
break;
case waveBits16:
mix16[outputnumchan-1]((signed short*)pringcur, (signed short*)pdest,
numsamp * outputspeak);
break;
}
pringcur += (numsamp << outputsampshift);
if (pringcur > pringend) portFatalError(badlog, 0);
if (pringcur == pringend) pringcur = pringbegin;
ringremain -= numsamp;
return;
}
static void mrMix(short remix)
{
long samplesplaying;
long origsamplesplaying;
long newsamplesplaying;
long samplestomix;
short blockstofill;
pmem pdest;
short i;
if (outputpaused) return;
waveGetSampleCount(&samplesplaying);
origsamplesplaying = samplesplaying;
if (remix) {
if (remixtimenummix)
{
samplesplaying -= (remixtimemix / remixtimenummix);
if (remixtimenumsound && ringnumnewsound)
samplesplaying -= (remixtimesound / remixtimenumsound) *
ringnumnewsound;
if (samplesplaying < 0) samplesplaying = 0;
}
ringremain += samplesplaying;
pringcur -= (samplesplaying << outputsampshift);
if (pringcur < pringbegin) pringcur += mixRingSize;
}
if (ringnumnewsound)
{
for (i=0; i<outputnumchan; i++)
{
if (ringisnewsound[i])
{
chanRewind(i, ringremain);
chanTruncate(i);
if (ringnewsound[i].pdata)
chanAddSound(i, ringnewsound + i);
mrReRender(i, ringremain);
ringisnewsound[i] = 0;
}
}
if (remix) {
waveGetSampleCount(&newsamplesplaying);
remixtimesound += (origsamplesplaying - newsamplesplaying);
remixtimenumsound += ringnumnewsound;
origsamplesplaying = newsamplesplaying;
}
ringnumnewsound = 0;
}
if (remix)
blockstofill = outputplayahead;
else {
blockstofill = 0;
while (samplesplaying <= outputblocksize * outputplayahead)
{
samplesplaying += outputblocksize;
blockstofill++;
}
blockstofill--;
}
if (blockstofill > 0)
{
mrRenderAll(outputblocksize * blockstofill);
while (blockstofill)
{
waveGetPlayPtr(&pdest);
samplestomix = (pringend - pringcur) >> outputsampshift;
if (samplestomix > ringremain) samplestomix = ringremain;
if (samplestomix > outputblocksize) samplestomix = outputblocksize;
mrLowMix(pdest, samplestomix);
pdest += (samplestomix << outputsampshift);
samplestomix = outputblocksize - samplestomix;
if (samplestomix) mrLowMix(pdest, samplestomix);
if (remix) {
waveGetSampleCount(&newsamplesplaying);
remixtimemix += (origsamplesplaying - newsamplesplaying);
remixtimenummix++;
remix = 0;
waveReset();
}
wavePlay();
blockstofill--;
}
}
if (remixtimenummix >= 128)
{
remixtimemix = remixtimemix * 32L / remixtimenummix;
remixtimenummix = 32;
}
if (remixtimenumsound >= 128)
{
remixtimesound = remixtimesound * 32L / remixtimenumsound;
remixtimenumsound = 32;
}
if (remixtimesound < 0) remixtimesound = 0;
if (remixtimemix < 0) remixtimemix = 0;
if (ringremain != 0) portFatalError(badlog, 0);
return;
}
void mrNewSound(short channum, pmixSound pms)
{
ringnewsound[channum] = *pms;
if (!ringisnewsound[channum]) ringnumnewsound++;
ringisnewsound[channum] = 1;
return;
}
/* -------- PUBLIC FUNCTIONS -------- */
rescode mixOpen(short numchan, waveSpeak ns, waveRate pr, waveBits pb)
{
outputnumchan = numchan;
outputpaused = 0;
outputrate = pr;
outputbits = pb;
outputspeak = ns;
if (waveOpen(pr,pb,ns, &outputblocksize, &outputplayahead,
&outputremix)) return badlog;
outputsampsize = (short)pb * (short)ns;
switch (outputsampsize)
{
case 1:
outputsampshift = 0;
break;
case 2:
outputsampshift = 1;
break;
case 4:
outputsampshift = 2;
break;
}
mrInit();
chanInit();
chanVerifyAll(0);
mixPump();
chanVerifyAll(0);
return 0;
}
rescode mixClose()
{
chanVerifyAll(0);
waveClose();
mrFree();
chanFree();
return 0;
}
rescode mixPlay(short chan, mixSound *pmix)
{
chanVerifyAll(0);
if (pmix->samples <= 0) return 0;
mrNewSound(chan, pmix);
chanVerifyAll(0);
if (outputremix) mrMix(1);
chanVerifyAll(0);
return 0;
}
rescode mixUnplay(pmem pdata)
{
short chan;
chanVerifyAll(0);
chan = chanFindData(pdata);
if (chan == -1) return 0;
mixSilence(chan);
chanVerifyAll(0);
return 0;
}
rescode mixAppend(short chan, mixSound *pmix)
{
chanVerifyAll(0);
if (pmix->samples <= 0) return 0;
if (outputremix && chanIsSilent(chan))
{
mrNewSound(chan, pmix);
chanVerifyAll(0);
mrMix(1);
}
else {
chanAddSound(chan, pmix);
chanVerifyAll(0);
mrMix(0);
}
chanVerifyAll(0);
return 0;
}
rescode mixSilence(short chan)
{
mixSound ms;
chanVerifyAll(0);
ms.pdata = 0;
mrNewSound(chan, &ms);
chanVerifyAll(0);
if (outputremix) mrMix(1);
chanVerifyAll(0);
return 0;
}
rescode mixIsSilent(short chan)
{
chanVerifyAll(0);
return chanIsSilent(chan);
}
rescode mixLoadChannel(short chan, pmixSound *pmix, short num)
{
/***/
return 0;
}
rescode mixSetVolume(short chan, pmixVol vol)
{
chanVerifyAll(0);
chanSetVol(chan, vol);
chanVerifyAll(0);
if (outputremix) mrMix(1);
chanVerifyAll(0);
return 0;
}
rescode mixGetVolume(short c, pmixVol pvol)
{
chanVerifyAll(0);
*pvol = chan[c].vol;
return 0;
}
rescode mixIsDataPlaying(pmem pdata)
{
short chan;
chanVerifyAll(0);
chan = chanFindData(pdata);
if (chan == -1) return 0;
return 1;
}
rescode mixPause()
{
chanVerifyAll(0);
outputpaused = 1;
return 0;
}
rescode mixResume()
{
chanVerifyAll(0);
outputpaused = 0;
return 0;
}
rescode mixPump()
{
chanVerifyAll(0);
mrMix(0);
chanVerifyAll(0);
return 0;
}
rescode mixVolCreate(pmixVol pmv, double vleft, double vright)
{
double vboth;
if (vleft < 1.0)
{
pmv->left.mul = (long) (vleft * 65536.0);
pmv->left.shift = 16;
}
else if (vleft == 1.0)
{
pmv->left.mul = 1;
pmv->left.shift = 0;
}
else {
pmv->left.mul = (long) (vleft * 256.0);
pmv->left.shift = 8;
}
if (vright < 1.0)
{
pmv->right.mul = (long) (vright * 65536.0);
pmv->right.shift = 16;
}
else if (vright == 1.0)
{
pmv->right.mul = 1;
pmv->right.shift = 0;
}
else {
pmv->right.mul = (long) (vright * 256.0);
pmv->right.shift = 8;
}
vboth = (vleft + vright) / (double)2.0;
if (vboth < 1.0)
{
pmv->both.mul = (long) (vboth * 65536.0);
pmv->both.shift = 16;
}
else if (vboth == 1.0)
{
pmv->both.mul = 1;
pmv->both.shift = 0;
}
else {
pmv->both.mul = (long) (vboth * 256.0);
pmv->both.shift = 8;
}
return 0;
}
#define mixVolSingle(dest, s1, s2) \
{ \
double f1,f2,fd; \
f1 = (double)(s1).mul / (double)(1L << (s1).shift); \
f2 = (double)(s2).mul / (double)(1L << (s2).shift); \
fd = f1 * f2; \
if (fd < 1.0) { \
(dest).mul = (long) (fd * 65536.0); \
(dest).shift = 16; \
} \
else if (fd == 1.0) { \
(dest).mul = 1; \
(dest).shift = 0; \
} \
else { \
(dest).mul = (long) (fd * 256.0); \
(dest).shift = 8; \
} \
}
rescode mixVolMult(pmixVol pmvDest, pmixVol pmvsrc1, pmixVol pmvsrc2)
{
mixVolSingle(pmvDest->left, pmvsrc1->left, pmvsrc2->left);
mixVolSingle(pmvDest->right, pmvsrc1->right, pmvsrc2->right);
mixVolSingle(pmvDest->both, pmvsrc1->both, pmvsrc2->both);
return 0;
}
rescode mixInstallCodec(pmixCodec pmc)
{
int i;
for (i=0; i<numcodecs; i++)
if (mixcodecs[i].type == pmc->type) return 1;
mixcodecs[numcodecs] = *pmc;
numcodecs++;
return 0;
}